package gui.gui2d;

import gui.gui2d.eventsmanager.MouseListenerChess;
import gui.gui2d.eventsmanager.Moviment;

import java.awt.*;
import java.awt.font.TextAttribute;
import java.awt.geom.Rectangle2D;



import java.io.File;
import java.io.IOException;
import java.text.AttributedString;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Observable;
import java.util.Observer;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import sun.org.mozilla.javascript.internal.xml.XMLLib.Factory;
import utility.Color;
import utility.EventClick;
import utility.EventPass;
import utility.Point;

import javax.imageio.ImageIO;
import javax.swing.JPanel;













import javax.swing.JPopupMenu;

import multiplayer.lan.Move;
import world.ChessBoardMenager;
import world.Piece;
import world.gameMenager.GameManager;
import world.gameMenager.GraphicsUpdatePromotion;
import world.pieces.Bishop;
import world.pieces.King;
import world.pieces.Knight;
import world.pieces.Pown;
import world.pieces.Queen;
import world.pieces.Rook;

public class ChessBoardPanel extends JPanel implements Observer ,Runnable{
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	Lock lock = new ReentrantLock();
	Condition moving = lock.newCondition();
	Image img ;
	Image imgOverBlack= Toolkit.getDefaultToolkit().createImage("images//MouseOverBlack.jpg");
	Image imgOverWhite= Toolkit.getDefaultToolkit().createImage("images//MouseOverWhite.jpg");
	private Shape [][] figure;
	private Piece piece;
	private int dimensionSquare=100;
	private int currentMouseOverX=-1;
	private int currentMouseOverY=-1;
	private Piece matrix[][];
	private utility.Point from;
	private utility.Point to;
	private ArrayList<utility.Point>possibleMoves;
	private ArrayList<utility.Point>possibleEates;
	private MouseListenerChess mlc;
	private boolean isMoving=false;
	Move movement;
	int xIntermedio;
	int yIntermedio;

	String carattere = "TimesRoman";
	int plain = Font.BOLD;
	int dimensioni = 100;
	Font fontCheck = new Font(carattere, plain, dimensioni);


	private Point pointCheck=null;
	private boolean isCheck=false;
	
	private boolean mate=false;
	
	private boolean connectionLost=false;
	private String connectionLostMessage;

	public int getDimensionSquare() {
		return dimensionSquare;
	}
	public ChessBoardPanel(ChessBoardMenager chessBoardMenager) {
		loadChessBoard();
		this.setLayout(new BorderLayout());
		this.setOpaque(false);
		this.possibleMoves=new ArrayList<utility.Point>();
		this.possibleEates=new ArrayList<utility.Point>();
		//this.gm=gm;
		loadMatrix(chessBoardMenager.getMatrix());
		figure=new Shape[8][8];
		Moviment listenerMoviment=new Moviment(dimensionSquare);
		mlc=new MouseListenerChess(dimensionSquare);
		listenerMoviment.addObserver(this);
		this.addMouseMotionListener(listenerMoviment);
		this.addMouseListener(mlc);
//		Thread t= new Thread(new Runnable() {
//			
//			@Override
//			public void run() {
//				try {
//					Thread.sleep(200);
//				} catch (InterruptedException e) {
//					// TODO Auto-generated catch block
//					e.printStackTrace();
//				}
//				repaint();
//				
//			}
//		});
//		t.start();
	}

	private void loadChessBoard() {
		try {
			img= ImageIO.read(new File("images//chessBoard.png"));
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		repaint();
		
	}
	public void disablePanelListener(){
		mlc.setEnable(false);

	}
	public void enablePanelListener(){
		mlc.setEnable(true);

	}

	private void loadMatrix(Piece[][] matrix2) {
		matrix=new Piece[8][8];
		for (int i = 0; i < matrix2.length; i++) {
			for (int j = 0; j < matrix2[0].length; j++) {
				if(matrix2[i][j]!=null){
					if(matrix2[i][j] instanceof Bishop){
						matrix[i][j]=new Bishop(	matrix2[i][j].getColor());
					}else if(matrix2[i][j] instanceof King){
						matrix[i][j]=new King(	matrix2[i][j].getColor());
					}else if(matrix2[i][j] instanceof Knight){
						matrix[i][j]=new Knight(	matrix2[i][j].getColor());
					}else if(matrix2[i][j] instanceof Pown){
						matrix[i][j]=new Pown(	matrix2[i][j].getColor());
					}else if(matrix2[i][j] instanceof Queen){
						matrix[i][j]=new Queen(	matrix2[i][j].getColor());
					}else if(matrix2[i][j] instanceof Rook){
						matrix[i][j]=new Rook(	matrix2[i][j].getColor());
					}
				}
			}
		}


	}
	public void addObserver(Observer o){
		mlc.addObserver(o);
	}
	@Override
	public void paintComponent(Graphics g){
		//TODO complete here
		super.paintComponent(g);

		Graphics2D g2 = (Graphics2D) g;
		drowSquare(g2);
		drowMouseOver(g2);
		drowSuggestsMove(g2);
		drowSuggestsEat(g2);
		check(g2);
		connectionLost(g2);
		drawPieces(g2);

		if(isMoving){
			drawPiecesWhithAnimation(g2, piece, xIntermedio, yIntermedio);
		}
		mate(g2);
		checkText(g2);

	}

	private void connectionLost(Graphics2D g2) {
		if(connectionLost){
			g2.setColor(new java.awt.Color(0,0,0,150));
			Rectangle2D rec= new Rectangle2D.Double(25,350 ,800,150);
			g2.fill(rec);
			g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
			AttributedString as = new AttributedString(connectionLostMessage);
			as.addAttribute(TextAttribute.FONT, fontCheck);
			g2.setColor(java.awt.Color.red);
			g2.drawString(as.getIterator(), 28, 450);
		}
		
	}
	private void mate(Graphics2D g2) {
		if(mate){
			g2.setColor(new java.awt.Color(0,0,0,150));
			Rectangle2D rec= new Rectangle2D.Double(110,350 ,630,150);
			g2.fill(rec);
			String s = "Scacco Matto!";
			g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
			AttributedString as = new AttributedString(s);
			as.addAttribute(TextAttribute.FONT, fontCheck);
			g2.setColor(java.awt.Color.red);
			g2.drawString(as.getIterator(), 120, 450);
		}
		
	}
	private void check(Graphics2D g2) {
		if(isCheck){
			g2.setColor(java.awt.Color.red);
			Rectangle2D rec= new Rectangle2D.Double(pointCheck.getY()*dimensionSquare+25,pointCheck.getX()*dimensionSquare+25 ,dimensionSquare,dimensionSquare);
			g2.fill(rec);
		}

	}
	
	private void checkText(Graphics2D g2) {
		if(isCheck){
			g2.setColor(new java.awt.Color(0,0,0,150));
			Rectangle2D rec= new Rectangle2D.Double(150,350 ,570,150);
			g2.fill(rec);
			String s = "Scacco al Re";
			g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
			AttributedString as = new AttributedString(s);
			as.addAttribute(TextAttribute.FONT, fontCheck);
			g2.setColor(java.awt.Color.red);
			g2.drawString(as.getIterator(), 160, 450);
		}

	}
	private void animation(Move movement) {
		try {
			lock.lock();
			while(isMoving){
				moving.await();
			}
			xIntermedio=movement.getFrom().getX()*100;
			yIntermedio=movement.getFrom().getY()*100;
			isMoving=true;
			Piece pFrom =matrix[movement.getFrom().getX()][movement.getFrom().getY()];
			this.piece=pFrom;
			from=new Point(movement.getFrom().getX(),movement.getFrom().getY());
			matrix[movement.getFrom().getX()][movement.getFrom().getY()]=null;
			this.movement=movement;
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			Thread t = new Thread(this);
			t.start();
		} catch (Exception e) {
			// TODO: handle exception
		}finally{
			lock.unlock();
		}
	}
	private void drowSuggestsEat(Graphics2D g2) {
		// TODO Auto-generated method stub
		// TODO Auto-generated method stub
		Iterator<utility.Point>it=possibleEates.iterator();
		while(it.hasNext()){
			utility.Point current=it.next();
			g2.setColor(java.awt.Color.red);
			Rectangle2D rec= new Rectangle2D.Double(current.getY()*dimensionSquare+25,current.getX()*dimensionSquare+25 ,dimensionSquare,dimensionSquare);
			g2.fill(rec);
		}
	}
	private void drowSuggestsMove(Graphics2D g2) {
		// TODO Auto-generated method stub
		Iterator<utility.Point>it=possibleMoves.iterator();
		while(it.hasNext()){
			utility.Point current=it.next();
			g2.setColor(java.awt.Color.blue);
			// Rectangle2Drec rec= new Rectangle2D.Double(current.getY()*dimentionSquare,current.getX()*dimentionSquare ,dimentionSquare,dimentionSquare);
			g2.drawOval((current.getY()*dimensionSquare)+55,(current.getX()*dimensionSquare)+55 ,dimensionSquare/3,dimensionSquare/3);

		}
	}
	private void drowMouseOver(Graphics2D g2) {

		if(currentMouseOverX!=-1){			
			if((currentMouseOverX+currentMouseOverY)%2==0){
				//System.out.println("bianco");
				g2.setColor(java.awt.Color.red);
				g2.drawRect( currentMouseOverX*dimensionSquare+25, currentMouseOverY*dimensionSquare+25,100,100);
				//g2.drawImage(imgOverWhite, currentMouseOverX*dimensionSquare+25, currentMouseOverY*dimensionSquare+25, null);
			}else{
				g2.setColor(java.awt.Color.blue);
				g2.drawRect( currentMouseOverX*dimensionSquare+25, currentMouseOverY*dimensionSquare+25,100,100);
				//System.out.println("nero");
				//g2.drawImage(imgOverBlack, currentMouseOverX*dimensionSquare+25, currentMouseOverY*dimensionSquare+25, null);
			}
		}
	}
	void drowSquare(Graphics2D g2){
		g2.drawImage(img, 0, 0, null);
	}
	public void drawPieces(Graphics2D g2 ){
		for(int i=0; i<matrix.length;i++){
			for(int j=0;j<matrix[0].length;j++){
				if(matrix[i][j]!=null){
					g2.drawImage(matrix[i][j].get2dImage(), (j*dimensionSquare)+25, (i*dimensionSquare)+25, null);
				}
			}
		}
	}
	public void drawPiecesWhithAnimation(Graphics2D g2,Piece piece ,int xi, int yi ){
		g2.drawImage(piece.get2dImage(), (yi)+25, (xi)+25, null);
	}
	public void movePiece(Point from , Point to){
		//enPassant
		Piece toMove=matrix[from.getX()][from.getY()];
		if(toMove instanceof Pown){
			if(matrix[to.getX()][to.getY()]==null){
				if(toMove.getColor().equals(Color.WHITE)){		
					if(from.getX()-to.getX()==1 && to.getY()-from.getY()==1 ){
						matrix[from.getX()][ from.getY()+1]=null;
					}
					if(from.getX()-to.getX()==1 && to.getY()-from.getY()==-1 ){
						matrix[from.getX()][ from.getY()-1]=null;
					}
				}else if(toMove.getColor().equals(Color.BLACK)){
					if(from.getX()-to.getX()==-1 && to.getY()-from.getY()==1 ){
						matrix[from.getX()][ from.getY()+1]=null;
					}
					if(from.getX()-to.getX()==-1 && to.getY()-from.getY()==-1 ){
						matrix[from.getX()][ from.getY()-1]=null;
					}
				}
			}
		}
		//enPassant
		animation(new Move(null, from, to));

	}

	@Override
	public void update(Observable o, Object arg) {
		// TODO Auto-generated method stub

		if(o instanceof Moviment){
			if(arg instanceof Point){
				Point p=(Point)arg;
				currentMouseOverX=p.getX();
				currentMouseOverY=p.getY();


				repaint();

			}
		}
		if(o instanceof GameManager){
			if(arg instanceof EventPass){
				EventPass ep= (EventPass) arg;
				if(ep.getRefer()==EventPass.FROM){
					this.from=(Point) ep.getPoint().clone();
				}else if(ep.getRefer()==EventPass.TO){
					this.to=(Point) ep.getPoint().clone();
					movePiece(from, to);
				}
			}
			if(arg instanceof Move){
				Move m = (Move) arg;
				movePiece(m.getFrom(), m.getTo());
			}
			if(arg instanceof EventClick){
				EventClick e = (EventClick) arg;
				GameManager g= (GameManager)o;
				if(e.getPoint()!=null){
					possibleEates.clear();
					possibleMoves.clear();
					calcolatePossibleMovesEats(g.getChess().getChessBoardMenager(),e.getPoint());
					repaint();
				}else{
					possibleEates.clear();
					possibleMoves.clear();
					repaint();
				}
			}
			if(arg instanceof Promotion){
				Promotion p = (Promotion) arg;
				matrix[p.getPositionPromotion().getX()][p.getPositionPromotion().getY()]=p.getPiecePromotion();
				repaint();
				enablePanelListener();
			}
		}
	}

	public void replace(GraphicsUpdatePromotion instance){
		Point position=instance.getPositionPromotion();
		Piece piece=instance.getPiecePromotion();
		matrix[position.getX()][position.getY()]=piece;
		repaint();
		enablePanelListener();
	}

	private void calcolatePossibleMovesEats(ChessBoardMenager chessBoardMenager, Point from) {
		for (int i = 0;  i< 8; i++) {
			for (int j = 0;j < 8; j++) {
				if(matrix[from.getX()][from.getY()] instanceof King){
					if(chessBoardMenager.canMovePieceKing(from, new Point(i, j)) && chessBoardMenager.isAllowed(from, new Point(i, j), matrix[from.getX()][from.getY()] , false)){
						possibleMoves.add(new Point(i, j));
					}
					if(chessBoardMenager.canMovePieceKing(from, new Point(i, j)) && chessBoardMenager.isAllowed(from, new Point(i, j), matrix[from.getX()][from.getY()] , true)){
						possibleEates.add(new Point(i, j));
					}
				}else{
					if(chessBoardMenager.canMovePiece(from, new Point(i, j))){
						possibleMoves.add(new Point(i, j));
					}
					if(chessBoardMenager.canEatPiece(from, new Point(i, j))){
						possibleEates.add(new Point(i, j));
					}
				}
			}
		}

	}
	@Override
	public void run() {
		lock.lock();
		int xi = movement.getFrom().getX()*dimensionSquare;
		int xj = movement.getFrom().getY()*dimensionSquare;
		int yi = movement.getTo().getX()*dimensionSquare;
		int yj = movement.getTo().getY()*dimensionSquare;
		for(float lambda=0;lambda<=1;lambda+=0.09){
			try {

				Thread.sleep(40);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			xIntermedio=(int) ((lambda*yi)+((1-lambda)*xi));
			yIntermedio=(int) ((lambda*yj)+((1-lambda)*xj));
			this.repaint();


		}
		matrix[movement.getTo().getX()][movement.getTo().getY()]=piece;
		moving.signalAll();
		isMoving=false;
		lock.unlock();
	}
	public void setCheck(utility.Color c, Point p) {
		animationCheck(c,p);
	}
	private void animationCheck(final Color c, final Point p) {
		Thread anime= new Thread(new Runnable() {
			@Override
			public void run() {
				if(c.equals(Color.BLACK)){
					pointCheck=p;
					for(int i = 0 ; i<3 ; i++){
						isCheck=true;
						repaint();
						try {
							Thread.sleep(700);
						} catch (InterruptedException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
						isCheck=false;
						repaint();
						try {
							Thread.sleep(700);
						} catch (InterruptedException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
					}
				}

			}
		});
		anime.start();

	}
	public void setMate() {
		mate=true;
		repaint();
		
	}
	public void setConnectionLost(String s) {
		connectionLostMessage=s;
		connectionLost=true;
		repaint();
	}
}
